package leetcode.concurrency;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * 哲学家进餐
 * @author jiangwz
 * @create 2019/11/7.
 */
public class Q1226 {

    public static void main(String[] args) throws InterruptedException {
        int n = 2;
        DiningPhilosophers diningPhilosophers = new DiningPhilosophers();
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    // 进餐次数
                    for (int j = 0; j < n; j++) {
                        diningPhilosophers.wantsToEat(
                                finalI,
                                () -> System.out.println("拿起左侧叉子"),
                                () -> System.out.println("拿起右侧叉子"),
                                () -> System.out.println("开始吃面"),
                                () -> System.out.println("放下左侧叉子"),
                                () -> System.out.println("放下右侧叉子")
                        );
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "thread-name").start();

        }

    }
}

//执行用时 :
//        16 ms
//        , 在所有 java 提交中击败了
//        12.68%
//        的用户
//        内存消耗 :
//        40.1 MB
//        , 在所有 java 提交中击败了
//        100.00%
//        的用户
class DiningPhilosophers {

    Semaphore s0 = new Semaphore(1);//叉子
    Semaphore s1 = new Semaphore(1);
    Semaphore s2 = new Semaphore(1);
    Semaphore s3 = new Semaphore(1);
    Semaphore s4 = new Semaphore(1);

    Semaphore[] array = new Semaphore[]{s0,s1,s2,s3,s4};

    public DiningPhilosophers() {

    }
    // call the run() method of any runnable to execute its code
    public void wantsToEat(int philosopher,
                           Runnable pickLeftFork,
                           Runnable pickRightFork,
                           Runnable eat,
                           Runnable putLeftFork,
                           Runnable putRightFork) throws InterruptedException {

        int leftForkIndex = getLeftForkIndex(philosopher); //哲学家左手叉子位置
        int rightForkIndex = getRightForkIndex(philosopher);//哲学家右手叉子位置
        while (true) {
            boolean tryAcquire = array[leftForkIndex].tryAcquire(5, TimeUnit.MILLISECONDS);
            if (tryAcquire) {
                // 拿起左手的叉子成功
                pickLeftFork.run();

                boolean tryAcquire1 = array[rightForkIndex].tryAcquire(5, TimeUnit.MILLISECONDS);
                if (tryAcquire1){
                    //拿起右手的叉子成功
                    pickRightFork.run();
                    //开吃
                    eat.run();
                    // 放下左手的叉子
                    putLeftFork.run();
                    // 放下右手的叉子
                    putRightFork.run();
                    //释放2个叉子的锁
                    array[leftForkIndex].release();
                    array[rightForkIndex].release();
                    // 吃完了结束循环
                    break;
                }else {
                    //没有拿到右手叉子,放下左手叉子,继续循环尝试
                    putLeftFork.run();
                    //释放左手的锁
                    array[leftForkIndex].release();
                }

            }
        }
    }

    /**
     * 左边叉子位置
     * @param n
     * @return
     */
    public int getLeftForkIndex(int n){
        if (n + 1 > 4) {
            n =0;
        }else {
            n+=1;
        }
        return n;
    }

    /**
     * 右侧叉子位置
     * @param n
     * @return
     */
    public int getRightForkIndex(int n){
        return n;
    }

}


//class DiningPhilosophers {
//
//
//    private Semaphore[] semaphoreArr = new Semaphore[]{
//            new Semaphore(1),
//            new Semaphore(1),
//            new Semaphore(1),
//            new Semaphore(1),
//            new Semaphore(1)
//    };
//
//    public DiningPhilosophers() {
//
//    }
//
//    // call the run() method of any runnable to execute its code
//    public void wantsToEat(int philosopher,
//                           Runnable pickLeftFork,
//                           Runnable pickRightFork,
//                           Runnable eat,
//                           Runnable putLeftFork,
//                           Runnable putRightFork) throws InterruptedException {
//        int leftPos = philosopher;
//        int rightPos = (philosopher + 4)%5;
//        int radomSeq = 0;
//        Semaphore semaphoreFist, semaphoreSecond;
//        Runnable fistPic, secondPic, fistPut, secondPut;
//        while(true){
//            // 随机方向开始
//            radomSeq = new Random().nextInt(2);
//            if(0 == radomSeq){
//                semaphoreFist = semaphoreArr[leftPos];
//                semaphoreSecond = semaphoreArr[rightPos];
//                fistPic = pickLeftFork;
//                secondPic = pickRightFork;
//                fistPut = putLeftFork;
//                secondPut = putRightFork;
//            } else{
//                semaphoreFist = semaphoreArr[rightPos];
//                semaphoreSecond = semaphoreArr[leftPos];
//                fistPic = pickRightFork;
//                secondPic = pickLeftFork;
//                fistPut = putRightFork;
//                secondPut = putLeftFork;
//            }
//
//            if(pickBysequence(semaphoreFist, semaphoreSecond, fistPic, secondPic, eat, fistPut, secondPut))
//                return;
//        }
//    }
//
//    private boolean pickBysequence(Semaphore semaphoreFist,
//                                   Semaphore semaphoreSecond,
//                                   Runnable fistPic,
//                                   Runnable secondPic,
//                                   Runnable eat,
//                                   Runnable fistPut,
//                                   Runnable secondPut) throws InterruptedException{
//
//        boolean permit = false, permitSec = false;
//        try {
//            // 获取第一根
//            permit = semaphoreFist.tryAcquire(5, TimeUnit.MILLISECONDS);
//            if (permit) {
//                // 拿第一根叉子
//                fistPic.run();
//                // 获取第二根叉子
//                permitSec = semaphoreSecond.tryAcquire(5, TimeUnit.MILLISECONDS);
//                if (permitSec) {
//                    // 获取成功
//                    secondPic.run();
//                    eat.run();
//                    fistPut.run();
//                    secondPut.run();
//                    return true;
//                } else {
//                    // 拿不到第二根，放下第一根
//                    fistPut.run();
//                    return false;
//                }
//            } else {
//                return false;
//            }
//        } catch (InterruptedException e) {
//            throw new IllegalStateException(e);
//        } finally {
//            if (permit) {
//                semaphoreFist.release();
//            }
//            if (permitSec) {
//                semaphoreSecond.release();
//            }
//        }
//
//    }
//}